using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Security.Encryption;
using CKY.AgentPlatform.Users;

namespace CKY.AgentPlatform.Security
{
    /// <summary>
    /// JWT令牌服务
    /// </summary>
    public class JwtTokenService : ITransientDependency
    {
        private readonly IConfiguration _configuration;
        private readonly IStringEncryptionService _stringEncryptionService;

        public JwtTokenService(
            IConfiguration configuration,
            IStringEncryptionService stringEncryptionService)
        {
            _configuration = configuration;
            _stringEncryptionService = stringEncryptionService;
        }

        /// <summary>
        /// 生成访问令牌
        /// </summary>
        /// <param name="user">用户信息</param>
        /// <param name="roles">角色列表</param>
        /// <param name="permissions">权限列表</param>
        /// <returns>JWT令牌</returns>
        public string GenerateAccessToken(PlatformUser user, List<string> roles, List<string> permissions)
        {
            var securityKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"] ?? "your-super-secret-key-with-at-least-32-characters"));
            
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

            var claims = new List<Claim>
            {
                new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
                new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
                new Claim(JwtRegisteredClaimNames.Email, user.Email),
                new Claim(JwtRegisteredClaimNames.GivenName, user.Name),
                new Claim(JwtRegisteredClaimNames.FamilyName, user.Surname),
                new Claim("avatar_url", user.AvatarUrl ?? ""),
                new Claim("department", user.Department ?? ""),
                new Claim("position", user.Position ?? ""),
                new Claim("is_admin", user.IsAdmin.ToString().ToLower()),
                new Claim("is_active", user.IsActive.ToString().ToLower()),
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            };

            // 添加角色声明
            foreach (var role in roles)
            {
                claims.Add(new Claim(ClaimTypes.Role, role));
            }

            // 添加权限声明
            foreach (var permission in permissions)
            {
                claims.Add(new Claim("permission", permission));
            }

            var token = new JwtSecurityToken(
                issuer: _configuration["Jwt:Issuer"],
                audience: _configuration["Jwt:Audience"],
                claims: claims,
                expires: DateTime.UtcNow.AddMinutes(
                    int.Parse(_configuration["Jwt:ExpirationInMinutes"] ?? "30")),
                signingCredentials: credentials
            );

            return new JwtSecurityTokenHandler().WriteToken(token);
        }

        /// <summary>
        /// 生成刷新令牌
        /// </summary>
        /// <param name="userId">用户ID</param>
        /// <returns>刷新令牌</returns>
        public string GenerateRefreshToken(Guid userId)
        {
            var randomNumber = new byte[64];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(randomNumber);
            
            var refreshToken = Convert.ToBase64String(randomNumber);
            
            // 可以将刷新令牌与用户ID关联存储在数据库中
            // 这里简单地将用户ID加密到刷新令牌中
            var tokenData = $"{userId}|{DateTime.UtcNow.AddDays(7)}|{refreshToken}";
            return _stringEncryptionService.Encrypt(tokenData);
        }

        /// <summary>
        /// 验证访问令牌
        /// </summary>
        /// <param name="token">JWT令牌</param>
        /// <returns>ClaimsPrincipal</returns>
        public ClaimsPrincipal ValidateAccessToken(string token)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"] ?? "your-super-secret-key-with-at-least-32-characters");

            try
            {
                var principal = tokenHandler.ValidateToken(token, new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = true,
                    ValidIssuer = _configuration["Jwt:Issuer"],
                    ValidateAudience = true,
                    ValidAudience = _configuration["Jwt:Audience"],
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero
                }, out var validatedToken);

                return principal;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 验证刷新令牌
        /// </summary>
        /// <param name="refreshToken">刷新令牌</param>
        /// <returns>用户ID</returns>
        public Guid? ValidateRefreshToken(string refreshToken)
        {
            try
            {
                var decryptedToken = _stringEncryptionService.Decrypt(refreshToken);
                var tokenParts = decryptedToken.Split('|');
                
                if (tokenParts.Length != 3)
                {
                    return null;
                }

                var userId = Guid.Parse(tokenParts[0]);
                var expiryDate = DateTime.Parse(tokenParts[1]);
                
                if (expiryDate < DateTime.UtcNow)
                {
                    return null;
                }

                return userId;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 从令牌中提取用户ID
        /// </summary>
        /// <param name="token">JWT令牌</param>
        /// <returns>用户ID</returns>
        public Guid? GetUserIdFromToken(string token)
        {
            var principal = ValidateAccessToken(token);
            var userIdClaim = principal?.FindFirst(JwtRegisteredClaimNames.Sub);
            
            return userIdClaim != null && Guid.TryParse(userIdClaim.Value, out var userId)
                ? userId
                : null;
        }

        /// <summary>
        /// 从令牌中提取用户名
        /// </summary>
        /// <param name="token">JWT令牌</param>
        /// <returns>用户名</returns>
        public string GetUserNameFromToken(string token)
        {
            var principal = ValidateAccessToken(token);
            return principal?.Identity?.Name;
        }

        /// <summary>
        /// 从令牌中提取权限
        /// </summary>
        /// <param name="token">JWT令牌</param>
        /// <returns>权限列表</returns>
        public List<string> GetPermissionsFromToken(string token)
        {
            var principal = ValidateAccessToken(token);
            var permissions = new List<string>();
            
            if (principal != null)
            {
                foreach (var claim in principal.FindAll("permission"))
                {
                    permissions.Add(claim.Value);
                }
            }
            
            return permissions;
        }

        /// <summary>
        /// 从令牌中提取角色
        /// </summary>
        /// <param name="token">JWT令牌</param>
        /// <returns>角色列表</returns>
        public List<string> GetRolesFromToken(string token)
        {
            var principal = ValidateAccessToken(token);
            var roles = new List<string>();
            
            if (principal != null)
            {
                foreach (var claim in principal.FindAll(ClaimTypes.Role))
                {
                    roles.Add(claim.Value);
                }
            }
            
            return roles;
        }

        /// <summary>
        /// 检查令牌是否即将过期
        /// </summary>
        /// <param name="token">JWT令牌</param>
        /// <param name="minutesBefore">提前分钟数</param>
        /// <returns>是否即将过期</returns>
        public bool IsTokenExpiringSoon(string token, int minutesBefore = 5)
        {
            try
            {
                var tokenHandler = new JwtSecurityTokenHandler();
                var jwtToken = tokenHandler.ReadJwtToken(token);
                
                var timeUntilExpiry = jwtToken.ValidTo - DateTime.UtcNow;
                return timeUntilExpiry.TotalMinutes <= minutesBefore;
            }
            catch
            {
                return false;
            }
        }
    }
}